<?php

/**
 * tpshop
 * ============================================================================
 * 版权所有 2017-2027 深圳搜豹网络科技有限公司，并保留所有权利。
 * 网站地址: http://www.tp-shop.cn
 * ----------------------------------------------------------------------------
 * 这不是一个自由软件！您只能在不用于商业目的的前提下对程序代码进行修改和使用 .
 * 不允许对程序代码以任何形式任何目的的再发布。
 * 如果商业用途务必到官方购买正版授权, 以免引起不必要的法律纠纷.
 * ============================================================================
 * author: lhb
 * Date: 2017-5-8
 */
namespace app\common\util;

use app\common\model\WxUser;
use think\Config;

class WechatAuthorUtil
{
    private $config = [];    //微信第三方平台配置
    private $errorMsg = '';  //错误字符串信息
    private $debug = false;   //是否开启调试
    private $tagsMap = null; //粉丝标签映射
    private $wx_user;
    
    public function __construct($config)
    {
        $this->wx_user = new WxUser;
        $this->config = $config;
    }
    
    public function getError() 
    {
        return $this->errorMsg;
    }
    
    private function setError($error)
    {
        if (!is_string($error)) {
            $error = json_encode($error, JSON_UNESCAPED_UNICODE);
        }
        $this->errorMsg = $error;
    }
    
    public function isDedug()
    {
        return $this->debug;
    }
    
    public function logDebugFile($content)
    {
        if (!$this->debug) {
            return;
        }
        if (!is_string($content)) {
            $content = json_encode($content, JSON_UNESCAPED_UNICODE);
        }
        file_put_contents("./wechat.log", date('Y-m-d H:i:s').' -- '.$content."\n", FILE_APPEND);
    }
    
    /**
     * http请求
     * @param type $url
     * @param type $method
     * @param type $fields
     * @return 
     */
    private function httpRequest($url, $method = 'GET', $fields = [])
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);

        $method = strtoupper($method);
        if ($method == 'GET' && !empty($fields)) {
            is_array($fields) && $fields = http_build_query($fields);
            $url = $url . (strpos($url,"?")===false ? "?" : "&") . $fields;
        }
        curl_setopt($ch, CURLOPT_URL, $url);

        if ($method != 'GET') {
            curl_setopt($ch, CURLOPT_POST, true);
            if (!empty($fields)) {
                $hadFile = false;
                if (is_array($fields)) {
                    /* 支持文件上传 */
                    if (class_exists('\CURLFile')) {
                        curl_setopt($ch, CURLOPT_SAFE_UPLOAD, true);
                        foreach ($fields as $key => $value) {
                            if ($this->isPostHasFile($value)) {
                                $fields[$key] = new \CURLFile(realpath(ltrim($value, '@')));
                                $hadFile = true;
                            }
                        }
                    } elseif (defined('CURLOPT_SAFE_UPLOAD')) {
                        if ($this->isPostHasFile($value)) {
                            curl_setopt($ch, CURLOPT_SAFE_UPLOAD, false);
                            $hadFile = true;
                        }
                    }
                }
                $fields = (!$hadFile && is_array($fields)) ? http_build_query($fields) : $fields;
                curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
            }
        }

        /* 关闭https验证 */
        if ("https" == substr($url, 0, 5)) {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        }

        $content = curl_exec($ch);
        curl_close($ch);

        return $content;
    }

    private function isPostHasFile($value)
    {
        if (is_string($value) && strpos($value, '@') === 0 && is_file(realpath(ltrim($value, '@')))) {
            return true;
        }
        return false;
    }
    
    /**
     * 专门用来检查微信接口返回值的。
     */
    private function requestAndCheck($url, $method = 'GET', $fields = [])
    {
        $return = $this->httpRequest($url, $method, $fields);
        if ($return === false) {
            if ($this->debug) {
                $this->setError("http请求出错！ " . Requests::$error);
            } else {
                $this->setError("http请求出错！");
            }
            return false;
        }
        
        $wxdata = json_decode($return, true);
        $this->debug && $this->logDebugFile(['url' => $url,'fields' => $fields,'wxdata' => $wxdata]);
        if (isset($wxdata['errcode']) && $wxdata['errcode'] != 0) {
            if ($wxdata['errcode'] == 40001) {
                $this->wx_user->save(['web_expires' => 0],['id'=>$this->config['id']]);//token错误
            }
            if ($this->debug) {
                $this->setError("微信错误代码：{$wxdata['errcode']};<br>错误信息：{$wxdata['errmsg']}<br>请求链接：$url");
            } else {
                $this->setError("操作失败，微信错误码：{$wxdata['errcode']};");
            }
            return false;
        }

        if (strtoupper($method) === 'GET' && empty($wxdata)) {
            if ($this->debug) {
                $this->setError("微信http请求返回为空！请求链接：$url");
            } else {
                $this->setError("微信http请求返回为空！操作失败");
            }
            return false;
        }

        return $wxdata;
    }

    /**
     * 获取第三方平台component_access_token
     * {
     *  "component_appid":"appid_value" , //第三方平台appid
     *  "component_appsecret": "appsecret_value", //第三方平台appsecret
     *  "component_verify_ticket": "ticket_value" //微信后台推送的ticket，此ticket会定时推送，具体请见本页的推送说明
     *  }
     */
    public function getComponentToken(){
        $wechat = $this->config;
        if (empty($wechat)) {
            $this->setError("第三方平台不存在！");
            return false;
        } 

        //判断是否过了缓存期
        $expire_time = $wechat['web_expires'];
        if($expire_time > time()){
           return $wechat['component_token'];
        }

        $component_appid = $wechat['appid'];
        $component_appsecret = $wechat['appsecret'];
        $component_verify_ticket = $wechat['component_verify_ticket'];
        $url = 'https://api.weixin.qq.com/cgi-bin/component/api_component_token';

        $post_arr = [
            "component_appid" => $component_appid,
            "component_appsecret" => $component_appsecret,
            "component_verify_ticket" => $component_verify_ticket,
        ];

        $post = json_encode($post_arr,JSON_UNESCAPED_UNICODE);

        $return = $this->requestAndCheck($url, 'POST',$post);
        if ($return === false) {
            return false;
        }

        $web_expires = time() + 7000; // 提前200秒过期
        $this->wx_user->save(['component_token'=>$return['component_access_token'], 'web_expires'=>$web_expires],['id'=>$wechat['id']]);
        $this->config['component_token'] = $return['component_access_token'];
        $this->config['web_expires'] = $web_expires;
        
        return $return['component_access_token'];

    }

    /**
     * 获取第三方平台预授权码pre_auth_code
     */
    public function getPreAuthCode(){
        $wechat = $this->config;
        $component_appid = $wechat['appid'];
        $component_token = $this->getComponentToken();
        if (!$component_token) {
            return false;
        }

        $url = 'https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token='.$component_token;

        $post_arr = [
            'component_appid' => $component_appid,
        ];

        $post = json_encode($post_arr,JSON_UNESCAPED_UNICODE);

        $return = $this->requestAndCheck($url, 'POST',$post);

        return $return;
    }

    /**
     * 使用授权码换取公众号或小程序的接口调用凭据和授权信息
     */
    public function query_auth($authorization_code){
        $wechat = $this->config;
        $component_appid = $wechat['appid'];
        $component_token = $this->getComponentToken();
        if (!$component_token) {
            return false;
        }

        $url = 'https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token='.$component_token;

        $post_arr = [
            'component_appid' => $component_appid,
            'authorization_code' => $authorization_code,
        ];

        $post = json_encode($post_arr,JSON_UNESCAPED_UNICODE);

        $return = $this->requestAndCheck($url,'POST',$post);

        file_put_contents('./requestMsg.log','return:'.$return['authorization_info']['authorizer_appid']."\n",FILE_APPEND);

        if($return === false){  
            return false;
        }
        

        return $return;
    }

    /**
     * 获取（刷新）授权公众号或小程序的接口调用凭据（令牌）
     * {
     *  "component_appid":"appid_value",
     *  "authorizer_appid":"auth_appid_value",
     *  "authorizer_refresh_token":"refresh_token_value",
     * }
     */
    public function flushAuthorCode($authorizer_info){
        $wechat = $this->config;
        $component_appid = $wechat['appid'];
        $component_token = $this->getComponentToken();
        if (!$component_token) {
            return false;
        }
        $url = 'https:// api.weixin.qq.com /cgi-bin/component/api_authorizer_token?component_access_token='.$component_token;

        $post_arr = [
            'component_appid' => $component_appid,
            'authorizer_appid' => $authorizer_info['appid'],
            'authorizer_refresh_token' => $authorizer_info['authorizer_refresh_token'],
        ];

        $post = json_encode($post_arr,JSON_UNESCAPED_UNICODE);

        $return = $this->requestAndCheck($url,'POST',$post);

        /**
         * {
         *  "authorizer_access_token" : "aaUl5s6kAByLwgV0BhXNuIFFUqfrR8vTATsoSHukcIGqJgrc4KmMJ-JlKoC_-NKCLBvuU1cWPv4vDcLN8Z0pn5I45mpATruU0b51hzeT1f8", 
         *  "expires_in" : 7200, 
         *  "authorizer_refresh_token" : "BstnRqgTJBXb9N2aJq6L5hzfJwP406tpfahQeLNxX0w"
         * }
         */

        return $return;

    }

    /**
     * 获取授权方的帐号基本信息
     */
    public function getAuthorizerInfo($authorizer_appid){
        $wechat = $this->config;
        $component_appid = $wechat['appid'];
        $component_token = $this->getComponentToken();
        if (!$component_token) {
            return false;
        }

        $url = 'https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token='.$component_token;

        $post_arr = [
            'component_appid' => $component_appid,
            'authorizer_appid' => $authorizer_appid,
        ];

        $post = json_encode($post_arr , JSON_UNESCAPED_UNICODE);
        $return = $this->requestAndCheck($url,'POST',$post);

        if($return === false){
            return false;
        }

        return $return;
    }

    /**
     * 获取授权方的选项设置信息
     * @param component_appid	第三方平台appid
     * @param authorizer_appid	授权公众号或小程序的appid
     * @param option_name	选项名称
     */
    public function getAuthorizerOption($authorizer_appid,$option_name){
        $wechat = $this->config;
        $component_appid = $wechat['appid'];
        $component_token = $this->getComponentToken();
        if (!$component_token) {
            return false;
        }
        $url = 'https://api.weixin.qq.com/cgi-bin/component/ api_get_authorizer_option?component_access_token='.$component_token;

        $post_arr = [
            'component_appid' => $component_appid,
            'authorizer_appid' => $authorizer_appid,
            'option_name' => $option_name,
        ];

        $post = json_encode($post_arr , JSON_UNESCAPED_UNICODE);
        $return = $this->requestAndCheck($url,'POST',$post);

        if($return === false){
            return false;
        }

        return $return;

    }

    /**
     * 设置授权方的选项信息
     * @param component_appid	第三方平台appid
     * @param authorizer_appid	授权公众号或小程序的appid
     * @param option_name	选项名称
     * @param option_value	设置的选项值
     */
    public function setAuthorizerOption($authorizer_appid,$option){
        $wechat = $this->config;
        $component_appid = $wechat['appid'];
        $component_token = $this->getComponentToken();
        if (!$component_token) {
            return false;
        }

        $url = 'https://api.weixin.qq.com/cgi-bin/component/ api_set_authorizer_option?component_access_token='.$component_token;

        $post_arr = [
            'component_appid' => $component_appid,
            'authorizer_appid' => $authorizer_appid,
            'option_name' => $option['option_name'],
            'option_value' => $option['option_value'],
        ];

        $post = json_encode($post_arr , JSON_UNESCAPED_UNICODE);
        $return = $this->requestAndCheck($url,'POST',$post);

        if($return === false){
            return false;
        }

        return true;
    }
}